{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./Resources/ai-sk-add-wechat.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Semantic Kernel 自由切换大模型\n",
    "上一节中，我们介绍了Semantic Kernel如何对接国内智谱AI大模型，但是国内大模型百花齐放，并不是一枝独秀。因此进行应用开发时，系统可能会选择对接多个模型提供商。那如何集成多个大模型并能在自由切换呢？这一节我们来详细展开说说。\n",
    "\n",
    "从上节可知，**对接国内大模型的主要思路是在OpenAI Connector的基础上来操作的，具体做法就是通过修改`Endpoint`或自定义`OpenAIClient`，但前提是国内大模型的API 要兼容OpenAI的 API。如果不兼容还可尝试代理方案，比如OneApi，查看目标大模型是否已被适配，若适配则直接用代理方案**。也就是说只要大模型的API最终能兼容OpenAI，那对接思路是一样的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 注册流行大模型\n",
    "\n",
    "就目前已知信息而言，通义千问、智谱AI、讯飞星火、月之暗面、零一万物提供了兼容OpenAI的API 格式。请按需访问以下开放平台注册账号并创建API Key。\n",
    "\n",
    "1. 讯飞星火开放平台：https://www.xfyun.cn\n",
    "2. 月之暗面开放平台：https://platform.moonshot.cn\n",
    "3. 零一万物开放平台：https://platform.lingyiwanwu.com\n",
    "4. 通义千问开放平台：https://dashscope.aliyun.com\n",
    "5. 智谱清言开放平台：https://open.bigmodel.cn\n",
    "6. etc."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 大模型平台抽象\n",
    "如果直接使用Http对接大模型，至少需要提供以下信息：\n",
    "1. Uri：请求地址，一个完整的请求地址又包含以下：( 以OpenAI的聊天补全接口为例：`https://api.openai.com/v1/chat/completions` )\n",
    "    * ApiEndpoint: `https://api.openai.com`\n",
    "    * ServicePath: `/v1/chat/completions`\n",
    "2. Model Id：模型编码\n",
    "3. Api Key：模型密钥\n",
    "\n",
    "但考虑到市面上有很多AI服务提供商，不同AI服务提供商的对接方式也各有不同，因此官方提供了多个Connectors，比如：\n",
    "* Microsoft.SemanticKernel.Connectors.OpenAI （√）\n",
    "* Microsoft.SemanticKernel.Connectors.AzureOpenAI（√）\n",
    "* Microsoft.SemanticKernel.Connectors.HuggingFace \n",
    "* Microsoft.SemanticKernel.Connectors.Google\n",
    "* Microsoft.SemanticKernel.Connectors.Onnx \n",
    "* Microsoft.SemanticKernel.Connectors.MistralAI\n",
    "* Microsoft.SemanticKernel.Connectors.Ollam \n",
    "\n",
    "因此首先需要抽象一个AI 服务提供商类型，对于没有单独Connector的AI 服务提供商，如果其API 格式是兼容OpenAI API 格式的，我们可以将其归类为`OpenAI_Compatible`类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "public enum AiProviderType\n",
    "{\n",
    "    OpenAI,\n",
    "    OpenAI_Compatible,\n",
    "    AzureOpenAI,\n",
    "    HuggingFace,\n",
    "    Google,\n",
    "    Onnx,\n",
    "    MistralAI,\n",
    "    Ollam\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "紧接着基于以上信息可以抽象以下模型：`AiProvider` 和`ApiService`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "public  class AiProvider\n",
    "{\n",
    "    /// <summary>\n",
    "    /// AI 服务提供商名称\n",
    "    /// </summary>\n",
    "    public string Name { get; set; }\n",
    "\n",
    "    /// <summary>\n",
    "    /// AI 服务提供商编码\n",
    "    /// </summary>\n",
    "    public string Code { get; set; }\n",
    "\n",
    "    public string ApiKey { get; set; }\n",
    "\n",
    "    public string ApiEndpoint { get; set; }    \n",
    "    \n",
    "    public  AiProviderType AiType { get; set; }\n",
    "\n",
    "    public List<ApiService> ApiServices { get; set; }\n",
    "\n",
    "    public ApiService? GetEmbeddingApiService() => GetApiService(\"embeddings\");\n",
    "\n",
    "    public ApiService? GetChatCompletionApiService() => GetApiService(\"chat-completions\");\n",
    "\n",
    "    private ApiService? GetApiService(string apiServiceName) => ApiServices.FirstOrDefault(x => x.Name == apiServiceName);\n",
    "}\n",
    "\n",
    "/// <summary>\n",
    "/// AI 服务提供商提供的API 服务\n",
    "/// </summary>\n",
    "/// <param name=\"Name\">Api名称</param>\n",
    "/// <param name=\"ModelId\">模型编码</param>\n",
    "///  <param name=\"Models\">可用模型列表</param>\n",
    "public record ApiService(string Name, string ModelId, string[]? ModelIds = null);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但对于一个应用来说，可以包含多个AI 提供商并指定默认的提供商，因此可以进一步抽象一个`AiOptions`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "public class AiOptions\n",
    "{\n",
    "    public string DefaultProvider { get; set; }\n",
    "    public List<AiProvider> Providers { get; set; }\n",
    "\n",
    "    public AiProvider? GetProvider(string providerCode) =>\n",
    "        Providers.FirstOrDefault(x => x.Code == providerCode);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "根据以上抽象，可以添加一个`Config`文件夹然后添加一个`appsettings.json`配置文件，其中配置如下：\n",
    "```\n",
    "{\n",
    "  \"AI\": {\n",
    "    \"DefaultProvider\": \"zhipu\",\n",
    "    \"Providers\": [\n",
    "      {\n",
    "        \"Name\": \"智谱\",\n",
    "        \"Code\": \"zhipu\",\n",
    "        \"AiType\": \"OpenAI_Compatible\",\n",
    "        \"ApiKey\": \"ddddc01549175d4c18e65a70e0d8329d.jAFiuW3FfK5PbxuE\",\n",
    "        \"ApiEndpoint\": \"https://open.bigmodel.cn/api/paas/v4\",\n",
    "        \"ApiServices\": [\n",
    "          {\n",
    "            \"Name\": \"chat-completions\",\n",
    "            \"ModelId\": \"glm-4\"\n",
    "          },\n",
    "          {\n",
    "            \"Name\": \"embeddings\",\n",
    "            \"ModelId\": \"embedding-3\"\n",
    "          }\n",
    "        ]\n",
    "      },{\n",
    "        \"Name\": \"月之暗面\",\n",
    "        \"Code\": \"moonshot\",\n",
    "        \"AiType\": \"OpenAI_Compatible\",\n",
    "        \"ApiKey\": \"sk-aPvBNM6rz76l7eCcq76f2i2BO3RrvxXITXgYvKT9gEX0y5ek\",\n",
    "        \"ApiEndpoint\":\"https://api.moonshot.cn\",\n",
    "        \"ApiServices\": [\n",
    "          {\n",
    "            \"Name\": \"chat-completions\",\n",
    "            \"ModelId\": \"moonshot-v1-8k\"\n",
    "          }\n",
    "        ]\n",
    "      },      \n",
    "      {\n",
    "        \"Name\": \"Azure OpenAI\",\n",
    "        \"Code\": \"azure-openai\",\n",
    "        \"ApiKey\": \"163ed6f8bc2947e0906d6ee5e173a222\",\n",
    "        \"ApiEndpoint\": \"https://my-openapi.openai.azure.com\",\n",
    "        \"ApiServices\": [\n",
    "          {\n",
    "            \"Name\": \"chat-completions\",\n",
    "            \"ModelId\": \"gpt-4o\"\n",
    "          },\n",
    "          {\n",
    "            \"Name\": \"embeddings\",\n",
    "            \"ModelId\": \"text-embedding-ada-002\"\n",
    "          }\n",
    "        ]\n",
    "      },\n",
    "      {\n",
    "        \"Name\": \"One Api\",\n",
    "        \"Code\": \"oneapi\",\n",
    "        \"AiType\": \"OpenAI_Compatible\",\n",
    "        \"ApiKey\": \"sk-9y9939P3ufwHaltcB95d91F3D9D64303Ad799e991f4700F1\",        \n",
    "        \"ApiEndpoint\": \"http://localhost:3000/v1\",\n",
    "        \"ApiServices\": [\n",
    "          {\n",
    "            \"Name\": \"chat-completions\",\n",
    "            \"ModelId\": \"lite\",\n",
    "            \"ModelIds\":[\"lite\",\"glm-4-flash\"]\n",
    "          }\n",
    "        ]\n",
    "      }\n",
    "  }      \n",
    "```\n",
    "\n",
    "从中可以看出共配置了两个AI 提供商：智谱和月之暗面，其中智谱配置了2个API 服务（`chat-completions`和`embedding`），月之暗面仅配置了一个API服务（`chat-completions`）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有了以上配置，就可以使用[Options Pattern in .NET](https://learn.microsoft.com/en-us/dotnet/core/extensions/options)来加载配置并绑定到对应的`AiOptions`。具体操作如下："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 安装以下NuGet包："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.Configuration, 8.0.0</span></li><li><span>Microsoft.Extensions.Configuration.Binder, 8.0.2</span></li><li><span>Microsoft.Extensions.Configuration.Json, 8.0.1</span></li><li><span>Microsoft.SemanticKernel, 1.25.0</span></li></ul></div></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#r \"nuget:Microsoft.Extensions.Configuration\"\n",
    "#r \"nuget:Microsoft.Extensions.Configuration.Json\"\n",
    "#r \"nuget:Microsoft.Extensions.Configuration.Binder\"\n",
    "#r \"nuget:Microsoft.SemanticKernel\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 添加工具类`AiSettings`用于加载配置，代码如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "using System.IO;\n",
    "using Microsoft.Extensions.Configuration;\n",
    "\n",
    "public static class AiSettings\n",
    "{\n",
    "    public static AiOptions LoadAiProvidersFromFile()\n",
    "    {\n",
    "        var configuration = new ConfigurationBuilder()\n",
    "            .SetBasePath(Directory.GetCurrentDirectory())\n",
    "            .AddJsonFile(\"Config/appsettings.json\", optional: true, reloadOnChange: true)\n",
    "            .Build();\n",
    "\n",
    "        var aiOptions = configuration.GetSection(\"AI\").Get<AiOptions>();\n",
    "        return aiOptions;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#39+AiOptions</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>DefaultProvider</td><td><div class=\"dni-plaintext\"><pre>zhipu</pre></div></td></tr><tr><td>Providers</td><td><table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#38+AiProvider</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>智谱</pre></div></td></tr><tr><td>Code</td><td><div class=\"dni-plaintext\"><pre>zhipu</pre></div></td></tr><tr><td>ApiKey</td><td><div class=\"dni-plaintext\"><pre>ddddc01549175d4c18e65a70e0d8329d.jAFiuW3FfK5PbxuE</pre></div></td></tr><tr><td>ApiEndpoint</td><td><div class=\"dni-plaintext\"><pre>https://open.bigmodel.cn/api/paas/v4</pre></div></td></tr><tr><td>AiType</td><td><span>OpenAI_Compatible</span></td></tr><tr><td>ApiServices</td><td><table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>ApiService { Name = chat-completions, ModelId = glm-4, ModelIds =  }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>chat-completions</pre></div></td></tr><tr><td>ModelId</td><td><div class=\"dni-plaintext\"><pre>glm-4</pre></div></td></tr><tr><td>ModelIds</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>ApiService { Name = embeddings, ModelId = embedding-3, ModelIds =  }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>embeddings</pre></div></td></tr><tr><td>ModelId</td><td><div class=\"dni-plaintext\"><pre>embedding-3</pre></div></td></tr><tr><td>ModelIds</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#38+AiProvider</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>月之暗面</pre></div></td></tr><tr><td>Code</td><td><div class=\"dni-plaintext\"><pre>moonshot</pre></div></td></tr><tr><td>ApiKey</td><td><div class=\"dni-plaintext\"><pre>sk-aPvBNM6rz76l7eCcq76f2i2BO3RrvxXITXgYvKT9gEX0y5ek</pre></div></td></tr><tr><td>ApiEndpoint</td><td><div class=\"dni-plaintext\"><pre>https://api.moonshot.cn/v1</pre></div></td></tr><tr><td>AiType</td><td><span>OpenAI_Compatible</span></td></tr><tr><td>ApiServices</td><td><table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>ApiService { Name = chat-completions, ModelId = moonshot-v1-8k, ModelIds =  }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>chat-completions</pre></div></td></tr><tr><td>ModelId</td><td><div class=\"dni-plaintext\"><pre>moonshot-v1-8k</pre></div></td></tr><tr><td>ModelIds</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table></td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#38+AiProvider</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>Azure OpenAI</pre></div></td></tr><tr><td>Code</td><td><div class=\"dni-plaintext\"><pre>azure-openai</pre></div></td></tr><tr><td>ApiKey</td><td><div class=\"dni-plaintext\"><pre>163ed6f8bc2947e0906d6ee5e173a222</pre></div></td></tr><tr><td>ApiEndpoint</td><td><div class=\"dni-plaintext\"><pre>https://my-openapi.openai.azure.com</pre></div></td></tr><tr><td>AiType</td><td><span>AzureOpenAI</span></td></tr><tr><td>ApiServices</td><td><table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>ApiService { Name = chat-completions, ModelId = gpt-4o, ModelIds =  }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>chat-completions</pre></div></td></tr><tr><td>ModelId</td><td><div class=\"dni-plaintext\"><pre>gpt-4o</pre></div></td></tr><tr><td>ModelIds</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>ApiService { Name = embeddings, ModelId = text-embedding-ada-002, ModelIds =  }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>embeddings</pre></div></td></tr><tr><td>ModelId</td><td><div class=\"dni-plaintext\"><pre>text-embedding-ada-002</pre></div></td></tr><tr><td>ModelIds</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table></td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Submission#38+AiProvider</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>One Api</pre></div></td></tr><tr><td>Code</td><td><div class=\"dni-plaintext\"><pre>oneapi</pre></div></td></tr><tr><td>ApiKey</td><td><div class=\"dni-plaintext\"><pre>sk-9y9939P3ufwHaltcB95d91F3D9D64303Ad799e991f4700F1</pre></div></td></tr><tr><td>ApiEndpoint</td><td><div class=\"dni-plaintext\"><pre>http://localhost:3000/v1</pre></div></td></tr><tr><td>AiType</td><td><span>OpenAI_Compatible</span></td></tr><tr><td>ApiServices</td><td><table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>ApiService { Name = chat-completions, ModelId = lite, ModelIds = System.String[] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>chat-completions</pre></div></td></tr><tr><td>ModelId</td><td><div class=\"dni-plaintext\"><pre>lite</pre></div></td></tr><tr><td>ModelIds</td><td><div class=\"dni-plaintext\"><pre>[ lite, glm-4-flash, lite, glm-4-flash ]</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table></td></tr></tbody></table></div></details></td></tr></tbody></table></td></tr></tbody></table></div></details><style>\r\n",
       ".dni-code-hint {\r\n",
       "    font-style: italic;\r\n",
       "    overflow: hidden;\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview {\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview td {\r\n",
       "    vertical-align: top;\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "details.dni-treeview {\r\n",
       "    padding-left: 1em;\r\n",
       "}\r\n",
       "table td {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "table tr { \r\n",
       "    vertical-align: top; \r\n",
       "    margin: 0em 0px;\r\n",
       "}\r\n",
       "table tr td pre \r\n",
       "{ \r\n",
       "    vertical-align: top !important; \r\n",
       "    margin: 0em 0px !important;\r\n",
       "} \r\n",
       "table th {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "// 测试配置读取\n",
    "var aiOptions= AiSettings.LoadAiProvidersFromFile();\n",
    "aiOptions.Display();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. 添加扩展方法来注册聊天补全服务："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "using Microsoft.SemanticKernel;\n",
    "using System.ClientModel;\n",
    "using OpenAI;\n",
    "\n",
    "public static IKernelBuilder AddChatCompletionService(this IKernelBuilder builder, string aiProviderCode = null)\n",
    "{\n",
    "    var aiOptions = AiSettings.LoadAiProvidersFromFile();\n",
    "    if (string.IsNullOrWhiteSpace(aiProviderCode))\n",
    "    {\n",
    "        aiProviderCode = aiOptions.DefaultProvider;\n",
    "    }\n",
    "    var aiProvider = aiOptions.Providers.FirstOrDefault(x => x.Code == aiProviderCode);\n",
    "    if (aiProvider == null)\n",
    "    {\n",
    "        throw new ArgumentException($\"未找到编码为 {aiProviderCode} 的 AI 服务提供商\");\n",
    "    }\n",
    "    var modelId = aiProvider.GetChatCompletionApiService()?.ModelId;\n",
    "    if (string.IsNullOrWhiteSpace(modelId))\n",
    "    {\n",
    "        throw new ArgumentException($\"未找到名称为 chat-completions 的 API 服务\");\n",
    "    }    \n",
    "\n",
    "    if (aiProvider.AiType == AiProviderType.OpenAI)\n",
    "    {\n",
    "        builder.AddOpenAIChatCompletion(\n",
    "            modelId: modelId,\n",
    "            apiKey: aiProvider.ApiKey);\n",
    "    }\n",
    "    \n",
    "    if (aiProvider.AiType == AiProviderType.AzureOpenAI)\n",
    "    {\n",
    "        builder.AddAzureOpenAIChatCompletion(\n",
    "            deploymentName: modelId,\n",
    "            endpoint: aiProvider.ApiEndpoint,\n",
    "            apiKey: aiProvider.ApiKey);\n",
    "    }\n",
    "\n",
    "    if (aiProvider.AiType == AiProviderType.OpenAI_Compatible)\n",
    "    {\n",
    "        OpenAIClientOptions clientOptions = new OpenAIClientOptions\n",
    "        {\n",
    "            Endpoint = new Uri(aiProvider.ApiEndpoint)\n",
    "        };\n",
    "\n",
    "        OpenAIClient client = new(new ApiKeyCredential(aiProvider.ApiKey), clientOptions);\n",
    "\n",
    "        builder.AddOpenAIChatCompletion(modelId: modelId, openAIClient: client);\n",
    "    }\n",
    "    \n",
    "    return builder;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "5. 注册AI 服务并测试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我是一个AI助手，旨在帮助回答问题、提供信息和协助解决问题。如果你有任何问题或需要帮助，请随时告诉我！\r\n"
     ]
    }
   ],
   "source": [
    "using Microsoft.SemanticKernel;\n",
    "using Microsoft.SemanticKernel.ChatCompletion;\n",
    "\n",
    "// 引入交互式的内核命名空间，以便用户输入\n",
    "using PolyglotKernel= Microsoft.DotNet.Interactive.Kernel;\n",
    "\n",
    "var aiProviderCode = await PolyglotKernel.GetInputAsync(\"请输入AI服务提供商编码：\");\n",
    "\n",
    "//Create Kernel builder\n",
    "var builder = Kernel.CreateBuilder();\n",
    "\n",
    "builder.AddChatCompletionService(aiProviderCode);\n",
    "\n",
    "var kernel = builder.Build();\n",
    "\n",
    "var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();\n",
    "var response = await chatCompletionService.GetChatMessageContentAsync(\"你是谁？\");\n",
    "Console.WriteLine(response.Content);"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "csharp"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
